import pickle

import taichi as ti

from abc import ABC, abstractmethod
from .utils import (
    Mat3x3,
    Vec2,
    Vec3,
    Vec4,
    Mat2x3,
    Mat3x2,
    Ray4,
    PI,
    randUnitVec2,
    deg2rad,
)
from .helper.log import Logger

__all__ = [
    "Camera",
]


@ti.data_oriented
class Camera(ABC):
    def __init__(self, size: "tuple[int, int]") -> None:
        self.size = self.w, self.h = size
        self.aspectRatio = self.w / self.h
        self.SSNumber = 5
        self.origin: ti.MatrixField = Vec4.field(shape=1)
        self.loadSetting()

    @abstractmethod
    def rayAt(self, uv: Vec2, t: int = 0) -> Ray4:
        """返回一条指向像素点(i, j)处的光线"""

    def keyBoardMsg(
        self, window: ti.ui.Window, lastCur
    ) -> "tuple[bool, tuple[float, float]]":
        """处理键盘消息"""
        keyTable = {
            'w': self.z,
            'a': -self.x,
            's': -self.z,
            'd': self.x,
            ti.ui.SPACE: self.y,
            ti.ui.SHIFT: -self.y,
            'q': Vec4(0, 0, 0, -1),
            'e': Vec4(0, 0, 0, 1),
        }
        dx = sum(keyTable[key] for key in keyTable if window.is_pressed(key))
        if window.is_pressed("Alt"):
            dx *= 16
        self.origin[0] += dx / 64
        clear = any(dx != Vec4(0))
        thisCur = window.get_cursor_pos()
        if thisCur != lastCur and window.is_pressed("RMB"):
            self.phi += (
                lastCur[0] - thisCur[0] if self.y.y > 0 else thisCur[0] - lastCur[0]
            )
            self.theta += lastCur[1] - thisCur[1]
            self.setScreen()
            clear = True
        return clear, thisCur

    def saveSetting(self):
        """保存镜头位置等参数到文件, 以便下次运行时相机还在同一位置"""
        with open("temp/cameraSetting.cfg", 'wb') as f:
            pickle.dump(
                {
                    "phi": self.phi,
                    "theta": self.theta,
                    "origin": list(self.origin[0]),
                },
                f,
            )
        return self

    def loadSetting(self):
        """从文件中加载镜头位置等参数, 如果找不到文件就设置默认值"""
        try:
            with open("temp/cameraSetting.cfg", 'rb') as f:
                cfg = pickle.load(f)
            self.tp(*cfg["origin"], cfg["theta"], cfg["phi"])
        except FileNotFoundError as e:
            Logger.warn(e)
            self.tp(0, 0, 0, 0, PI / 2, PI / 2)
        return self

    def tp(self, x, y, z, t, theta=None, phi=None):
        self.origin[0] = Vec4(x, y, z, t)
        if theta is not None:
            self.theta = theta
        if phi is not None:
            self.phi = phi

    def makeSettingPlane(self, gui: ti.ui.Gui) -> bool:
        """生成参数修改面板, 返回是否需要重新绘制图像"""
        return False
